﻿using System;
using System.Collections.Generic;
using System.Linq;
using ChinaMobile.XinJu;
using Coder.Workflow;
using Coder.Workflow.Stores;
using Fee.MingTong;
using Fee.Orders.DesignWorkflow;
using Fee.Stores;
using Fee.VendorMatchers;
using Identity;
using Microsoft.EntityFrameworkCore;
using Text.Helper;

namespace Fee.Orders.PreWorkflow
{
    public class PreDelgateOrderManager<T> : WorkflowManager<T, PreDelegateOrder>
        where T : FeeDbContext
    {
        private readonly BudgetOrderManager<T> _budgetOrderManager;
        private readonly IConstructionTaskStore<T> _constructionTaskStore;
        private readonly T _context;
        private readonly IFrameworkContractStore<T> _frameworkContractStore;
        private readonly IVendorMatcherStore<T> _matcherStore;
        private readonly IPreDelegateOrderStore<T> _preDelegateOrderStore;
        private readonly IStationStore<T> _stationStore;
        private readonly IWorkActivityStore<T> _workActivityStore;
        private readonly MingTongManager _mingTongManager;
        private readonly IWorkProcessStore<T> _workProcessStore;


        public PreDelgateOrderManager(T context, IVendorMatcherStore<T> matcherStore,
            IPreDelegateOrderStore<T> preDelegateOrderStore,
            IFrameworkContractStore<T> frameworkContractStore, IStationStore<T> stationStore,
            IConstructionTaskStore<T> constructionTaskStore, IWorkProcessStore<T> workProcessStore,
            BudgetOrderManager<T> budgetOrderManager,
            IWorkActivityStore<T> workActivityStore,
            MingTongManager mingTongManager
        ) : base(context, workActivityStore)
        {
            _context = context;
            _matcherStore = matcherStore;
            _preDelegateOrderStore = preDelegateOrderStore;
            _frameworkContractStore = frameworkContractStore;
            _stationStore = stationStore;
            _constructionTaskStore = constructionTaskStore;
            _workProcessStore = workProcessStore;
            _budgetOrderManager = budgetOrderManager;
            _workActivityStore = workActivityStore;
            _mingTongManager = mingTongManager;
        }

        public IEnumerable<WorkActivity> GetMyTask(DelegateWorkActivitySearch searcher
            , out int total)
        {
            var query = searcher.ForDelegateBase(_context);

            total = query.Count();

            query = _workActivityStore.IncludeAllDelegateBase(query);

            return query.Skip(searcher.GetSkip()).Take(searcher.GetTake()).ToList();
        }

        private bool ValidateStation(int stationId, out string message,
            out Station station)
        {
            station = _stationStore.GetById(stationId);

            message = null;
            if (station == null)
            {
                message = "站点不存在";
                return false;
            }

            if (station.Status != StationStatus.S1)
            {
                message = "站点只有在状态为'" + StationStatus.S1.GetEnumDisplayName() + " 才能创建订单,现在状态是 " +
                          station.Status.GetEnumDisplayName();
                return false;
            }

            return true;
        }

        private bool ValidateConstructionTask(int stationId, out string message, out ConstructionTask task, out PreDelegateOrder order)
        {
            order = null;
            task = _constructionTaskStore.GetByStation(stationId);
#if DEBUG
            //if (task == null)
            //    throw new Exception("task is null");
            if (task != null)
                foreach (var item in task.Details)
                {
                    if (item.Project == null)
                        throw new Exception("ConstructionTask.Project should not be empty");
                    if (item.Contract == null)
                        throw new Exception("ConstructionTask.Contract should not be empty");


                }

#endif

            if (task != null)

            {
                var taskId = task.Id;
                order = _context.PreDelegateOrders.FirstOrDefault(f => f.ConstructionTask.Id == taskId);
                if (order != null && order.Status != ProcessInstanceStatus.Created)
                {
                    message = "站点有委托了，因此不能创建新的委托单。";
                    return false;
                }
            }


            message = null;
            return true;
        }

        public PreDelegateResult CreateOrder(int stationId, string userName,
            DateTimeOffset signTime, string thirdPartyWorkflowOrderId)
        {
            var user = _context.GetByUserName(userName);
            var succssed = ValidateStation(stationId, out var errorMessage, out var station);

            var delegateResult = new PreDelegateResult
            {
                Message = errorMessage,
                Successed = succssed
            };
            if (!delegateResult.Successed) return delegateResult;

            delegateResult.Successed = ValidateConstructionTask(stationId, out errorMessage, out var srcTask, out var preDelegatOrder);

            delegateResult.Message = errorMessage;

#if DEBUG
            if (srcTask != null)
                foreach (var detail in srcTask?.Details)
                    if (detail.Project == null)
                        throw new Exception("从ConstructionTask获取Details，但是Detail的Project为空.");
#endif

            if (!delegateResult.Successed) return delegateResult;
            var wf = _workProcessStore.Get<PreDelegateWorkProcess>();
            var details = srcTask == null || srcTask.Details.Count == 0
                ? BuildByMatcher(signTime, station)
                : srcTask.Details.Select(f => f.CreateOrderDetail());

            matcherProject(station, details);


            var delegateOrder = preDelegatOrder ?? wf.CreateDelegateOrderBy(station, user, details);

            delegateResult.Order = delegateOrder;
            delegateResult.Order.ThirdPartyWorkflowId = thirdPartyWorkflowOrderId;
            return delegateResult;
        }

        private IEnumerable<DelegateOrderDetail> BuildByMatcher(DateTimeOffset signTime, Station station)
        {
            var result = new List<DelegateOrderDetail>();
            var matchers = _matcherStore.GetByDistricts(signTime.Year, new int?[] { station.District.Id });

            var vendorMatchers = matchers as VendorMatcher[] ?? matchers.ToArray();

            foreach (ContractType contractType in Enum.GetValues(typeof(ContractType)))
                foreach (ConstructionType constructionType in Enum.GetValues(typeof(ConstructionType)))
                    foreach (var matcher in vendorMatchers)
                        if (matcher.TryMatch(contractType, station, constructionType,
                            _frameworkContractStore, signTime.Year, out var detail))
                        {
                            result.Add(detail);
                            break;
                        }

            return result;
        }

        private void matcherProject(Station station, IEnumerable<DelegateOrderDetail> delegateOrderDetails)
        {
            var projectSettings = _context.Set<ProjectSetting>()
                .ToDictionary(f => f.StationType.ToString() + f.ConstructionType, f => f.ProjectNumber);

            foreach (var delegateDetail in delegateOrderDetails)
            {
                var key = station.Type.ToString() + delegateDetail.ConstructionType;
                if (projectSettings.ContainsKey(key))
                    delegateDetail.Project = _context.Projects.FirstOrDefault(f => f.No == projectSettings[key]);
            }
        }

        protected override void OnProcessComplete(ProcessInstance instance)
        {
            base.OnProcessComplete(instance);
            var preDelegateOrder = (PreDelegateOrder)instance;
            if (preDelegateOrder.ThirdPartyWorkflowId != null)
                _mingTongManager.PreOrderSubmit(preDelegateOrder);
        }

        public PreDelegateResult OrderConfirm(PreDelegateOrder order,
            IEnumerable<DelegateOrderDetail> details)
        {
            var wf = _workProcessStore.Get<PreDelegateWorkProcess>();

            var result = wf.CreateDelegateOrderBy(order.ConstructionTask.Station, order.User, details);


            return new PreDelegateResult
            {
                Successed = true,
                Order = result,
                Message = "成功"
            };
        }


        protected override void AssignByResolve(IEnumerable<WorkActivity> assignTo, WorkActivity resolveWorkActivity)
        {
            var workTask = assignTo.First().WorkTask;
            if (workTask.Name == PreDelegateOrder.NotifyDesigner)
            {
                var queue = new Queue<WorkActivity>(assignTo.ToArray());
                var instance = assignTo.First().ProcessInstance as PreDelegateOrder;
                var vendors = instance.Details.Where(f => f.ContractType == ContractType.Design)
                    .Select(f => f.Contract.Vendor).Distinct().ToList();
                foreach (var vendorUser in vendors)
                {
                    var user = _context.GetByVendorName(vendorUser);
                    if (user == null) throw new OrderException("供应商没有生成账号-" + vendorUser.Name);

                    queue.Dequeue().AssignTo(user);
                }
            }
        }

        public override IEnumerable<WorkActivity> Start(PreDelegateOrder order)
        {
            if (order.ConstructionTask == null) _context.Entry(order).Reference(f => f.ConstructionTask).Load();
            order.ConstructionTask.Status = ConstructionTaskStatus.PreAuthing;
            return base.Start(order);
        }

        protected override void Resolve(WorkActivity activity, string comment,
            out IEnumerable<WorkActivity> nextWorkActivities)
        {
            base.Resolve(activity, comment, out nextWorkActivities);
            _context.SaveChanges(); //constructionTaskDetail 需要生成id。
            // 如果流程结束，那么自动启动第二个流程 预算审核。
            if (activity.ProcessInstance.Status == ProcessInstanceStatus.Completed)
            {
                var preOrder = (PreDelegateOrder)activity.ProcessInstance;

                _context.Entry(activity.DepositUser).Reference(f => f.Vendor).Load(); //加载一下vendor；

                var constryctionType = preOrder.ConstructionTask.Details
                    .Where(_ => _.Contract.Vendor.Id == activity.DepositUser.Vendor.Id).Select(f => f.ConstructionType);
                var systemUser = _context.SystemUser();
                var delegateOrder = _budgetOrderManager.CreateOrder(preOrder.ConstructionTask, systemUser,
                    constryctionType.ToArray());
                var activities = _budgetOrderManager.Start(delegateOrder);
                foreach (var acti in activities) acti.AssignTo(activity.DepositUser);
            }
        }

        protected override IQueryable<WorkActivity> FillQuery(IQueryable<WorkActivity> workActivities)
        {
            return workActivities
                .Include(f => (PreDelegateOrder)f.ProcessInstance).ThenInclude(f => f.Details)
                .ThenInclude(f => f.Project)
                .Include(f => (PreDelegateOrder)f.ProcessInstance).ThenInclude(f => f.Details)
                .ThenInclude(f => f.Contract).ThenInclude(f => f.Vendor)
                .Include(f => (PreDelegateOrder)f.ProcessInstance).ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.Station).ThenInclude(f => f.District)
                .Include(f => (PreDelegateOrder)f.ProcessInstance).ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.Station).ThenInclude(f => f.OrderDistrict)
                .Include(f => (PreDelegateOrder)f.ProcessInstance).ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.Details).ThenInclude(f => f.Contract)
               .Include(f => (PreDelegateOrder)f.ProcessInstance)
                .ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.Details).ThenInclude(f => f.Contract).ThenInclude(f => f.Vendor)
                .Include(f => (PreDelegateOrder)f.ProcessInstance)
                .ThenInclude(f => f.Details).ThenInclude(f => f.Contract)

            ;
        }

        public PreDelegateResult UpdateWorkContent(IEnumerable<DelegateOrderDetail> details,
            int preDelegateOrderId, User user)
        {
            var fr = GetCurrentWorkActivity(preDelegateOrderId, user);

            if (fr != null && !fr.Any())
                return new PreDelegateResult
                {
                    Successed = false,
                    Message = "工作已经完成"
                };
            var order = _preDelegateOrderStore.GetById(preDelegateOrderId);
            var activity = fr.First();
            if (activity.DepositUser.Id != user.Id && order.User.Id != user.Id)
                return new PreDelegateResult
                {
                    Successed = false,
                    Message = "不是你的工作"
                };


            order.UpdateBy(details);
            _context.Update(order);
            _context.SaveChanges();

            return new PreDelegateResult
            {
                Successed = true,
                Message = "保存成功"
            };
        }
    }
}